﻿#region Namespaces

using System;
using System.Collections.Generic;
using System.Linq;

using Vibstudio.X4NET.Globalization;
using Vibstudio.X4NET.Xml.Linq;
using Vibstudio.X4NET.Xml.XPath;

#endregion

namespace Vibstudio.X4NET.Xml.Extraction
{
    public class XmlExtractor : IXmlExtraction
    {
        public OperationResult<XPathList> GetXPaths(string xml, bool positionalFormat = true, bool textualFormat = false, bool omitDeclarations = false)
        {
            return GetXPaths(xml.ToXDocument(), positionalFormat, textualFormat, omitDeclarations);
        }

        public OperationResult<XPathList> GetXPaths(byte[] bytes, bool positionalFormat = true, bool textualFormat = false, bool omitDeclarations = false)
        {
            return GetXPaths(bytes.ToXDocument(), positionalFormat, textualFormat, omitDeclarations);
        }

        public OperationResult<XPathList> GetXPaths(XDocument document, bool positionalFormat = true, bool textualFormat = false, bool omitDeclarations = false)
        {
            try
            {
                XPathList xpaths = document.ExtractXPaths(positionalFormat, textualFormat);

                return BuildResult(positionalFormat, omitDeclarations, xpaths);
            }
            catch (Exception exception)
            {
                return OperationResult<XPathList>.CreateFailure(exception.Message);
            }
        }

        public OperationResult<XPathList> GetXPaths(Uri uri, bool positionalFormat = true, bool textualFormat = false, bool omitDeclarations = false)
        {
            try
            {
                XPathList xpaths = XDocument.Load(uri.LocalPath).ExtractXPaths(positionalFormat, textualFormat);

                if (omitDeclarations)
                {
                    xpaths = xpaths.StripDeclarations();
                }

                if (positionalFormat)
                {
                    return OperationResult<XPathList>.CreateSuccess(xpaths);
                }

                IList<string> duplicates = xpaths.GroupBy(x => x).Where(g => g.Count() > 1).Select(g => g.Key).ToList();
                return duplicates.Any()
                           ? OperationResult<XPathList>.CreateSuccess(xpaths, Labeling.Factory.Get.PositionalExtractionRequired.Message)
                           : OperationResult<XPathList>.CreateSuccess(xpaths);
            }
            catch (Exception exception)
            {
                return OperationResult<XPathList>.CreateFailure(exception.Message);
            }
        }

        #region Helpers

        private OperationResult<XPathList> BuildResult(bool positional, bool omitXmlDeclaration, XPathList xpaths)
        {
            if (omitXmlDeclaration)
            {
                xpaths = xpaths.StripDeclarations();
            }

            if (positional)
            {
                return OperationResult<XPathList>.CreateSuccess(xpaths);
            }

            IList<string> duplicates = xpaths.GroupBy(x => x).Where(g => g.Count() > 1).Select(g => g.Key).ToList();
            return duplicates.Any()
                       ? OperationResult<XPathList>.CreateSuccess(xpaths, Labeling.Factory.Get.PositionalExtractionRequired.Message)
                       : OperationResult<XPathList>.CreateSuccess(xpaths);
        }

        #endregion
    }
}